import pandas as pd
import numpy as np
from textblob import TextBlob  # 用于政策情绪分析

def factor_bond_trading_signals(
    clean_price: pd.Series,
    yield_: pd.Series,
    modified_duration: pd.Series,
    convexity: pd.Series,
    corporate_spread: pd.Series,
    policy_sentiment: pd.Series,
    duration_adj: pd.Series,
    credit_spread_chg: pd.Series,
    max_drawdown: float = 0.1
) -> pd.Series:
    """
    生成国债交易信号的量化因子函数

    参数:
    ----------
    clean_price : pd.Series
        债券净价序列（不含应计利息）
    yield_ : pd.Series
        债券收益率序列（注意避免关键字冲突）
    modified_duration : pd.Series
        修正久期序列
    convexity : pd.Series
        凸性序列
    corporate_spread : pd.Series
        公司债利差（相对国债）
    policy_sentiment : pd.Series
        央行政策情绪文本（需情感分析）
    duration_adj : pd.Series
        久期调整因子
    credit_spread_chg : pd.Series
        信用利差变化
    max_drawdown : float, optional
        最大允许回撤阈值（默认0.1）

    返回:
    ----------
    pd.Series
        标准化交易信号序列（-1到1之间）

    逻辑说明:
    ----------
    1. 收益率曲线因子：结合久期和凸性计算收益率变动敏感性
    2. 信用利差因子：监控公司债与国债利差变化
    3. 政策情绪因子：通过NLP分析央行政策文本情绪
    4. 风险控制模块：通过最大回撤约束调整信号强度
    5. 信号合成：加权综合各因子，最后进行标准化处理

    注意:
    ----------
    - 所有输入序列需为日频且已对齐
    - 收益率上升时信号为负（价格下跌预期）
    - 最终信号经过sigmoid压缩保证在[-1,1]区间
    """

    # ======================
    # 1. 收益率曲线因子
    # ======================
    # 计算收益率变动对价格的敏感性（二阶近似）
    price_sensitivity = (-modified_duration * yield_.diff()) + \
                        0.5 * convexity * (yield_.diff()**2)

    # 标准化处理
    yield_factor = price_sensitivity.rolling(5).mean() / \
                  price_sensitivity.rolling(20).std()
    yield_factor = yield_factor.replace([np.inf, -np.inf], 0).fillna(0)

    # ======================
    # 2. 信用利差因子
    # ======================
    # 动态利差变化监测（3日动量 vs 20日均值）
    spread_momentum = corporate_spread.diff(3) / corporate_spread.rolling(10).std()
    spread_factor = (spread_momentum - credit_spread_chg) / \
                   (corporate_spread + 1e-6)  # 避免除零

    # ======================
    # 3. 政策情绪因子
    # ======================
    def analyze_sentiment(text):
        """使用TextBlob进行英文文本情感分析（中文需替换其他库）"""
        if pd.isna(text): return 0
        return TextBlob(str(text)).sentiment.polarity

    sentiment_scores = policy_sentiment.apply(analyze_sentiment)
    policy_factor = sentiment_scores.rolling(3).mean() * duration_adj

    # ======================
    # 4. 风险控制模块
    # ======================
    # 计算历史回撤
    rolling_max = clean_price.expanding().max()
    drawdown = (clean_price - rolling_max) / rolling_max

    # 生成风险调整系数（回撤超阈值时降低仓位）
    risk_coef = np.where(drawdown < -max_drawdown,
                        0.5,  # 超过回撤阈值时减半仓位
                        1.0)

    # ======================
    # 5. 信号合成
    # ======================
    # 因子加权（权重可参数化优化）
    raw_signal = 0.4 * yield_factor + \
                0.3 * spread_factor + \
                0.3 * policy_factor

    # 应用风险控制
    adjusted_signal = raw_signal * risk_coef

    # Sigmoid压缩到[-1,1]区间
    def sigmoid(x):
        return 2 / (1 + np.exp(-x)) - 1

    final_signal = adjusted_signal.apply(sigmoid)

    # 极值处理
    final_signal = np.clip(final_signal, -1, 1)

    return final_signal.rename('trading_signal')

# 使用示例
# signals = factor_bond_trading_signals(
#     df['clean_price'], df['yield'], df['modified_duration'],
#     df['convexity'], df['corporate_spread'], df['policy_sentiment'],
#     df['duration_adj'], df['credit_spread_chg']
# )



# 外汇因子挖掘结果
import pandas as pd
import numpy as np
from typing import Union

def factor_currency_momentum_carry(
    open: pd.Series,
    close: pd.Series,
    high: pd.Series,
    low: pd.Series,
    bid_qty: pd.Series,
    ask_qty: pd.Series,
    bid_ask_spread: pd.Series,
    swap_rate: pd.Series,
    swift_volume: pd.Series,
    order_imbalance: pd.Series,
    currency_pair: str,
    leverage_ratio: float = 1.0,
    lookback_period: int = 5,
    max_drawdown: float = 0.1
) -> pd.Series:
    """
    外汇动量套利因子（Momentum + Carry Trade）

    因子逻辑：
    1. 动量部分：计算过去N日的收益率动量（考虑高低波动范围）
    2. 套利部分：考虑隔夜利息成本（swap_rate）和杠杆调整
    3. 流动性调整：结合订单簿不平衡和买卖价差
    4. 风险控制：根据最大回撤限制调整信号幅度

    参数：
    open, close, high, low: OHLC价格序列
    bid_qty, ask_qty: 最优买卖档量
    bid_ask_spread: 买卖价差(单位：pip)
    swap_rate: 隔夜利息点数（正数表示多头持仓成本）
    swift_volume: SWIFT系统成交量
    order_imbalance: 订单不平衡度 (bid_qty - ask_qty)/(bid_qty + ask_qty)
    currency_pair: 货币对名称（如'EURUSD'）
    leverage_ratio: 杠杆倍数（用于调整carry收益）
    lookback_period: 动量计算周期（默认5日）
    max_drawdown: 最大允许回撤（默认10%）

    返回：
    pd.Series: 标准化交易信号（-1到1之间）
    """

    # ===================== 1. 动量计算 =====================
    # 真实波动范围（ATR风格）
    true_range = pd.concat([high - low,
                          high - close.shift(1),
                          close.shift(1) - low], axis=1).max(axis=1)
    normalized_range = true_range / close  # 波动率标准化

    # 价格动量（考虑波动率调整）
    returns = close.pct_change(lookback_period)
    momentum = returns / (normalized_range.rolling(lookback_period).mean() + 1e-6)

    # ===================== 2. 套利计算 =====================
    # 隔夜利息调整（考虑杠杆）
    # swap_rate通常为正表示持有成本，负表示收益
    carry_adjusted = -swap_rate * leverage_ratio / close  # 转换为收益率

    # ===================== 3. 流动性评估 =====================
    # 订单簿不平衡度（-1到1之间）
    imbalance_score = order_imbalance.rolling(3).mean()

    # 买卖价差惩罚项（价差越大惩罚越大）
    spread_penalty = np.exp(-bid_ask_spread / bid_ask_spread.median())

    # ===================== 4. 信号合成 =====================
    raw_signal = (momentum * 0.6 + carry_adjusted * 0.4) * imbalance_score * spread_penalty

    # ===================== 5. 风险控制 =====================
    # 波动率调整
    vol_adjusted = raw_signal / raw_signal.abs().rolling(10).max()

    # 最大回撤约束
    max_position = 1.0 / max_drawdown
    final_signal = np.clip(vol_adjusted * max_position, -1, 1)

    # 添加元信息
    final_signal.name = f"factor_currency_momentum_carry_{currency_pair}"

    return final_signal


def factor_orderbook_alpha(
    bid_qty: pd.Series,
    ask_qty: pd.Series,
    bid_ask_spread: pd.Series,
    swift_volume: pd.Series,
    order_imbalance: pd.Series,
    currency_pair: str,
    max_drawdown: float = 0.1
) -> pd.Series:
    """
    订单簿L2特征因子

    因子逻辑：
    1. 基于订单簿不平衡度的短期反转信号
    2. 结合SWIFT成交量确认大额资金流向
    3. 买卖价差动态阈值过滤

    参数：
    bid_qty, ask_qty: 最优买卖档量
    bid_ask_spread: 买卖价差(单位：pip)
    swift_volume: SWIFT系统成交量
    order_imbalance: 订单不平衡度
    currency_pair: 货币对名称
    max_drawdown: 最大回撤限制

    返回：
    pd.Series: 标准化交易信号
    """

    # 异常值处理
    valid_imbalance = np.where(
        (bid_qty > 0) & (ask_qty > 0),
        order_imbalance,
        0
    )

    # 成交量加权不平衡度
    volume_weight = np.log1p(swift_volume)
    weighted_imbalance = valid_imbalance * volume_weight

    # 价差过滤（当价差超过中位数2倍时不交易）
    spread_threshold = bid_ask_spread.median() * 2
    spread_filter = np.where(
        bid_ask_spread <= spread_threshold,
        1.0,
        0.0
    )

    # 反转信号（当前不平衡与昨日对比）
    reversal = -weighted_imbalance.diff()

    # 标准化处理
    normalized_signal = reversal / reversal.abs().rolling(10).max()

    # 风险调整
    risk_adjusted = normalized_signal * (1 - max_drawdown)

    risk_adjusted.name = f"factor_orderbook_alpha_{currency_pair}"
    return risk_adjusted


def factor_macro_policy_integration(
    close: pd.Series,
    macro_indicator: Union[pd.Series, None] = None,
    policy_sentiment: Union[pd.Series, None] = None,
    currency_pair: str = "EURUSD",
    max_drawdown: float = 0.1
) -> pd.Series:
    """
    宏观政策整合因子

    参数：
    close: 收盘价序列
    macro_indicator: 宏观经济指标（如利差）
    policy_sentiment: 央行政策情绪得分（来自文本分析）
    currency_pair: 货币对
    max_drawdown: 最大回撤

    返回：
    pd.Series: 宏观政策综合信号
    """
    # 默认处理（示例仅用价格数据）
    if macro_indicator is None:
        macro_indicator = pd.Series(0, index=close.index)

    if policy_sentiment is None:
        policy_sentiment = pd.Series(0, index=close.index)

    # 宏观指标动量（假设指标增大有利于本币升值）
    macro_momentum = macro_indicator.pct_change(5)

    # 政策情绪平滑
    policy_smooth = policy_sentiment.rolling(3).mean()

    # 价格趋势确认
    price_trend = np.sign(close.rolling(5).mean().diff())

    # 信号合成
    combined = 0.4*macro_momentum + 0.5*policy_smooth + 0.1*price_trend

    # 风险调整
    final_signal = np.tanh(combined) * (1 - max_drawdown)
    final_signal.name = f"factor_macro_policy_{currency_pair}"

    return final_signal

